using System;
using System.Collections;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Drawing;
using System.Reflection;
using System.IO;

namespace TwainLib
{
public enum TwainCommand
	{
	Not				= -1,
	Null			= 0,
	TransferReady	= 1,
	CloseRequest	= 2,
	CloseOk			= 3,
	DeviceEvent		= 4
	}




public static class Twain
	{
	private const short CountryUSA		= 1;
	private const short LanguageUSA		= 13;

    public static void FillIdentity(TwIdentity appid)
    {
        appid.Id = IntPtr.Zero;
        appid.Version.MajorNum = 1;
        appid.Version.MinorNum = 1;
        appid.Version.Language = LanguageUSA;
        appid.Version.Country = CountryUSA;
        appid.Version.Info = "1.0";
        appid.ProtocolMajor = TwProtocol.Major;
        appid.ProtocolMinor = TwProtocol.Minor;
        appid.SupportedGroups = (int)(TwDG.Image | TwDG.Control);
        appid.Manufacturer = "Zoo# Tech";
        appid.ProductFamily = "HomeDMS";
        appid.ProductName = "HomeDMS";

    }

	 static Twain()
		{
            //appid = new TwIdentity();
            //FillIdentity(appid);
            //srcds = new TwIdentity();
            //srcds.Id = IntPtr.Zero;

            evtmsg.EventPtr = Marshal.AllocHGlobal(Marshal.SizeOf(winmsg));
        }

    //~Twain()
    //    {
    //    Marshal.FreeHGlobal( evtmsg.EventPtr );
    //    }




    //public void Init( IntPtr hwndp )
    //    {
    //    Finish();
    //    TwRC rc = DSMparent( appid, IntPtr.Zero, TwDG.Control, TwDAT.Parent, TwMSG.OpenDSM, ref hwndp );
    //    if( rc == TwRC.Success )
    //        {
    //        rc = DSMident( appid, IntPtr.Zero, TwDG.Control, TwDAT.Identity, TwMSG.GetDefault, srcds );
    //        if( rc == TwRC.Success )
    //            hwnd = hwndp;
    //        else
    //            rc = DSMparent( appid, IntPtr.Zero, TwDG.Control, TwDAT.Parent, TwMSG.CloseDSM, ref hwndp );
    //        }
    //    }

     [StructLayout(LayoutKind.Sequential, Pack = 2)]
     public class BITMAPINFOHEADER
     {
         public int biSize = 0;
         public int biWidth = 0;
         public int biHeight = 0;
         public short biPlanes = 0;
         public short biBitCount = 0;
         public int biCompression = 0;
         public int biSizeImage = 0;
         public int biXPelsPerMeter = 0;
         public int biYPelsPerMeter = 0;
         public int biClrUsed = 0;
         public int biClrImportant = 0;
     }

     private static IntPtr GetPixelInfo(IntPtr pDib, out Rectangle bmpRect)
     {
         BITMAPINFOHEADER bmi = new BITMAPINFOHEADER();
         Marshal.PtrToStructure(pDib, bmi);
         bmpRect = new Rectangle();
         bmpRect.X = bmpRect.Y = 0;
         bmpRect.Width = bmi.biWidth;
         bmpRect.Height = bmi.biHeight;
         if (bmi.biSizeImage == 0)
             bmi.biSizeImage = ((((bmi.biWidth * bmi.biBitCount) + 31) & ~31) >> 3) * bmi.biHeight;
         int p = bmi.biClrUsed;
         if ((p == 0) && (bmi.biBitCount <= 8))
             p = 1 << bmi.biBitCount;
         p = (p * 4) + bmi.biSize + (int)pDib;
         return (IntPtr)p;
     }

     [DllImport("gdi32.dll", ExactSpelling = true)]
     internal static extern int SetDIBitsToDevice(IntPtr hdc, int xdst, int ydst,
        int width, int height, int xsrc, int ysrc, int start, int lines,
        IntPtr bitsptr, IntPtr bmiptr, int color);

     public static Bitmap CreateBitmapFromDib1(IntPtr hDib)
     {
         if (hDib == IntPtr.Zero)
             return null;
         IntPtr pDib = GlobalLock(hDib);
         try
         {
             Rectangle bmpRect;
             IntPtr pPix = GetPixelInfo(pDib, out bmpRect);
             Bitmap _scannedImage = new Bitmap(bmpRect.Width, bmpRect.Height);
             Graphics scannedImageGraphics = Graphics.FromImage(_scannedImage);
             IntPtr hdc = scannedImageGraphics.GetHdc();
             SetDIBitsToDevice(hdc, 0, 0, bmpRect.Width, bmpRect.Height, 0, 0, 0, bmpRect.Height, pPix, pDib, 0);
             scannedImageGraphics.ReleaseHdc(hdc);
             return _scannedImage;
         }
         finally
         {
             //GlobalFree(pDib);
             GlobalUnlock(hDib);
         }
     }

    // @@ ** seems wrong implementation
    public static Bitmap CreateBitmapFromDib(IntPtr hDIB)
    {
        // We create a new Bitmap File in memory.    
        // This is the easiest way to convert a DIB to Bitmap.    
        // No PInvoke needed.
        if (hDIB == IntPtr.Zero)
            return null;
        IntPtr pDIB = Twain.GlobalLock(hDIB);
        try
        {
            byte[] dibBytes = new byte[Twain.GlobalSize(hDIB)];
            Marshal.Copy(pDIB, dibBytes, 0, dibBytes.Length);
            Stream dib = new MemoryStream(dibBytes);
            dib.Position = 0;
            BinaryReader reader = new BinaryReader(dib);
            int headerSize = reader.ReadInt32();
            int pixelSize = (int)dib.Length - headerSize;
            int fileSize = 14 + headerSize + pixelSize;
            MemoryStream bmp = new MemoryStream(fileSize);
            BinaryWriter writer = new BinaryWriter(bmp);
            // 1. Write Bitmap File Header:			     
            writer.Write((byte)'B');
            writer.Write((byte)'M');
            writer.Write(fileSize);
            writer.Write((int)0);
            writer.Write(14 + headerSize);
            // 2. Copy the DIB     
            dib.Position = 0;
            byte[] data = new byte[(int)dib.Length];
            dib.Read(data, 0, (int)dib.Length);
            writer.Write(data, 0, (int)data.Length);
            // 3. Create a new Bitmap from our new stream:    
            bmp.Position = 0;
            dib.Close();
            return new Bitmap(bmp);
        }
        finally
        {
            Twain.GlobalFree(pDIB);
        }
    }    

    [DllImport("GdiPlus.dll",
   CharSet = CharSet.Unicode, ExactSpelling = true)]
    private static extern int GdipCreateBitmapFromGdiDib(IntPtr pBIH,
                              IntPtr pPix, out IntPtr pBitmap);


    public static Bitmap BitmapFromDIB(IntPtr pDIB, IntPtr pPix)
    {
        MethodInfo mi = typeof(Bitmap).GetMethod("FromGDIplus",
                        BindingFlags.Static | BindingFlags.NonPublic);
        if (mi == null)
            return null; // (permission problem) 

        IntPtr pBmp = IntPtr.Zero;
        int status = GdipCreateBitmapFromGdiDib(pDIB, pPix, out pBmp);

        if ((status == 0) && (pBmp != IntPtr.Zero)) // success 
            return (Bitmap)mi.Invoke(null, new object[] { pBmp });

        else
            return null; // failure 
    }

    //public void Select()
    //    {
    //    TwRC rc;
    //    CloseSrc();
    //    if( appid.Id == IntPtr.Zero )
    //        {
    //        Init( hwnd );
    //        if( appid.Id == IntPtr.Zero )
    //            return;
    //        }
    //    rc = DSMident( appid, IntPtr.Zero, TwDG.Control, TwDAT.Identity, TwMSG.UserSelect, srcds );
    //    }


    //public void Acquire()
    //    {
    //    TwRC rc;
    //    CloseSrc();
    //    if( appid.Id == IntPtr.Zero )
    //        {
    //        Init( hwnd );
    //        if( appid.Id == IntPtr.Zero )
    //            return;
    //        }
    //    rc = DSMident( appid, IntPtr.Zero, TwDG.Control, TwDAT.Identity, TwMSG.OpenDS, srcds );
    //    if( rc != TwRC.Success )
    //        return;

    //    TwCapability cap = new TwCapability( TwCap.XferCount, 1 );
    //    rc = DScap( appid, srcds, TwDG.Control, TwDAT.Capability, TwMSG.Set, cap );
    //    if( rc != TwRC.Success )
    //        {
    //        CloseSrc();
    //        return;
    //        }

    //    TwUserInterface	guif = new TwUserInterface();
    //    guif.ShowUI = 1;
    //    guif.ModalUI = 1;
    //    guif.ParentHand = hwnd;
    //    rc = DSuserif( appid, srcds, TwDG.Control, TwDAT.UserInterface, TwMSG.EnableDS, guif );
    //    if( rc != TwRC.Success )
    //        {
    //        CloseSrc();
    //        return;
    //        }
    //    }


    //public ArrayList TransferPictures()
    //    {
    //    ArrayList pics = new ArrayList();
    //    if( srcds.Id == IntPtr.Zero )
    //        return pics;

    //    TwRC rc;
    //    IntPtr hbitmap = IntPtr.Zero;
    //    TwPendingXfers pxfr = new TwPendingXfers();
		
    //    do
    //        {
    //        pxfr.Count = 0;
    //        hbitmap = IntPtr.Zero;

    //        TwImageInfo	iinf = new TwImageInfo();
    //        rc = DSiinf( appid, srcds, TwDG.Image, TwDAT.ImageInfo, TwMSG.Get, iinf );
    //        if( rc != TwRC.Success )
    //            {
    //            CloseSrc();
    //            return pics;
    //            }

    //        rc = DSixfer( appid, srcds, TwDG.Image, TwDAT.ImageNativeXfer, TwMSG.Get, ref hbitmap );
    //        if( rc != TwRC.XferDone )
    //            {
    //            CloseSrc();
    //            return pics;
    //            }

    //        rc = DSpxfer( appid, srcds, TwDG.Control, TwDAT.PendingXfers, TwMSG.EndXfer, pxfr );
    //        if( rc != TwRC.Success )
    //            {
    //            CloseSrc();
    //            return pics;
    //            }

    //        pics.Add( hbitmap );
    //        }
    //    while( pxfr.Count != 0 );

    //    rc = DSpxfer( appid, srcds, TwDG.Control, TwDAT.PendingXfers, TwMSG.Reset, pxfr );
    //    return pics;
    //    }


	public static TwainCommand PassMessage( ref Message m, TwIdentity app, TwIdentity ds )
		{
		int pos = GetMessagePos();

		winmsg.hwnd		= m.HWnd;
		winmsg.message	= m.Msg;
		winmsg.wParam	= m.WParam;
		winmsg.lParam	= m.LParam;
		winmsg.time		= GetMessageTime();
		winmsg.x		= (short) pos;
		winmsg.y		= (short) (pos >> 16);
		
		Marshal.StructureToPtr( winmsg, evtmsg.EventPtr, false );
		evtmsg.Message = 0;
		TwRC rc = DSevent( app, ds, TwDG.Control, TwDAT.Event, TwMSG.ProcessEvent, ref evtmsg );
        if (rc == TwRC.Failure)
        {
            TwStatus st = new TwStatus();
            DSstatus(app, ds, TwDG.Control, TwDAT.Status, TwMSG.Get, st);
        }
		if( rc == TwRC.NotDSEvent )
			return TwainCommand.Not;
		if( evtmsg.Message == (short) TwMSG.XFerReady )
			return TwainCommand.TransferReady;
		if( evtmsg.Message == (short) TwMSG.CloseDSReq )
			return TwainCommand.CloseRequest;
		if( evtmsg.Message == (short) TwMSG.CloseDSOK )
			return TwainCommand.CloseOk;
		if( evtmsg.Message == (short) TwMSG.DeviceEvent )
			return TwainCommand.DeviceEvent;

		return TwainCommand.Null;
		}

    //public void CloseSrc()
    //    {
    //    TwRC rc;
    //    if( srcds.Id != IntPtr.Zero )
    //        {
    //        TwUserInterface	guif = new TwUserInterface();
    //        rc = DSuserif( appid, srcds, TwDG.Control, TwDAT.UserInterface, TwMSG.DisableDS, guif );
    //        rc = DSMident( appid, IntPtr.Zero, TwDG.Control, TwDAT.Identity, TwMSG.CloseDS, srcds );
    //        }
    //    }

    //public void Finish()
    //    {
    //    TwRC rc;
    //    CloseSrc();
    //    if( appid.Id != IntPtr.Zero )
    //        rc = DSMparent( appid, IntPtr.Zero, TwDG.Control, TwDAT.Parent, TwMSG.CloseDSM, ref hwnd );
    //    appid.Id = IntPtr.Zero;
    //    }

    //public IntPtr		hwnd;
    //public TwIdentity appid;
    //public TwIdentity srcds;
    public static TwEvent evtmsg;
    public static WINMSG winmsg;
	


	// ------ DSM entry point DAT_ variants:
		[DllImport("twain_32.dll", EntryPoint="#1")]
	public static extern TwRC DSMparent( [In, Out] TwIdentity origin, IntPtr zeroptr, TwDG dg, TwDAT dat, TwMSG msg, ref IntPtr refptr );

		[DllImport("twain_32.dll", EntryPoint="#1")]
	public static extern TwRC DSMident( [In, Out] TwIdentity origin, IntPtr zeroptr, TwDG dg, TwDAT dat, TwMSG msg, [In, Out] TwIdentity idds );

		[DllImport("twain_32.dll", EntryPoint="#1")]
	public static extern TwRC DSMstatus( [In, Out] TwIdentity origin, IntPtr zeroptr, TwDG dg, TwDAT dat, TwMSG msg, [In, Out] TwStatus dsmstat );


	// ------ DSM entry point DAT_ variants to DS:
		[DllImport("twain_32.dll", EntryPoint="#1")]
	public static extern TwRC DSuserif( [In, Out] TwIdentity origin, [In, Out] TwIdentity dest, TwDG dg, TwDAT dat, TwMSG msg, TwUserInterface guif );

		[DllImport("twain_32.dll", EntryPoint="#1")]
	public static extern TwRC DSevent( [In, Out] TwIdentity origin, [In, Out] TwIdentity dest, TwDG dg, TwDAT dat, TwMSG msg, ref TwEvent evt );

		[DllImport("twain_32.dll", EntryPoint="#1")]
	public static extern TwRC DSstatus( [In, Out] TwIdentity origin, [In] TwIdentity dest, TwDG dg, TwDAT dat, TwMSG msg, [In, Out] TwStatus dsmstat );

		[DllImport("twain_32.dll", EntryPoint="#1")]
	public static extern TwRC DScap( [In, Out] TwIdentity origin, [In] TwIdentity dest, TwDG dg, TwDAT dat, TwMSG msg, [In, Out] TwCapability capa );

		[DllImport("twain_32.dll", EntryPoint="#1")]
	public static extern TwRC DSiinf( [In, Out] TwIdentity origin, [In] TwIdentity dest, TwDG dg, TwDAT dat, TwMSG msg, [In, Out] TwImageInfo imginf );

		[DllImport("twain_32.dll", EntryPoint="#1")]
	public static extern TwRC DSixfer( [In, Out] TwIdentity origin, [In] TwIdentity dest, TwDG dg, TwDAT dat, TwMSG msg, ref IntPtr hbitmap );

		[DllImport("twain_32.dll", EntryPoint="#1")]
	public static extern TwRC DSpxfer( [In, Out] TwIdentity origin, [In] TwIdentity dest, TwDG dg, TwDAT dat, TwMSG msg, [In, Out] TwPendingXfers pxfr );


		[DllImport("kernel32.dll", ExactSpelling=true)]
	internal static extern IntPtr GlobalAlloc( int flags, int size );

    [DllImport("kernel32.dll", ExactSpelling = true)]
        internal static extern int GlobalSize(IntPtr handle);

		[DllImport("kernel32.dll", ExactSpelling=true)]
	internal static extern IntPtr GlobalLock( IntPtr handle );
		[DllImport("kernel32.dll", ExactSpelling=true)]
	internal static extern bool GlobalUnlock( IntPtr handle );
		[DllImport("kernel32.dll", ExactSpelling=true)]
	internal static extern IntPtr GlobalFree( IntPtr handle );

		[DllImport("user32.dll", ExactSpelling=true)]
	private static extern int GetMessagePos();
		[DllImport("user32.dll", ExactSpelling=true)]
	private static extern int GetMessageTime();


		[DllImport("gdi32.dll", ExactSpelling=true)]
	private static extern int GetDeviceCaps( IntPtr hDC, int nIndex );

		[DllImport("gdi32.dll", CharSet=CharSet.Auto)]
	private static extern IntPtr CreateDC( string szdriver, string szdevice, string szoutput, IntPtr devmode );

		[DllImport("gdi32.dll", ExactSpelling=true)]
	private static extern bool DeleteDC( IntPtr hdc );




	public static int ScreenBitDepth {
		get {
			IntPtr screenDC = CreateDC( "DISPLAY", null, null, IntPtr.Zero );
			int bitDepth = GetDeviceCaps( screenDC, 12 );
			bitDepth *= GetDeviceCaps( screenDC, 14 );
			DeleteDC( screenDC );
			return bitDepth;
			}
		}


		[StructLayout(LayoutKind.Sequential, Pack=4)]
	public struct WINMSG
		{
		public IntPtr		hwnd;
		public int			message;
		public IntPtr		wParam;
		public IntPtr		lParam;
		public int			time;
		public int			x;
		public int			y;
		}


	} // class Twain
}
